// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT

#include <arch/defines.h>
#include <arch/x86/asm.h>
#include <arch/x86/descriptor.h>
#include <arch/x86/mmu.h>
#include <arch/x86/registers.h>
#include <asm.h>
#include <zircon/tls.h>
#include <mexec.h>

#define ENTRY64_ADDR 0x100020

.section .text
FUNCTION_LABEL(mexec_asm)
    /* Stash all the arguments passed in registers R8 - R13 */
    mov %r9,  %r13   /* Unused Arg */
    mov %r8,  %r12   /* Memmove Ops */
    mov %rcx, %r11   /* Unused Arg */
    mov %rdx, %r10   /* Unused Arg */
    mov %rsi, %r9    /* CR3 for Safe page tables */
    mov %rdi, %r8    /* Bootimage Address */

    /* Switch to the safe identity mapped page tables */
    mov  %r9, %cr3

    /* Load the kernel relocation op into ram */
    mov MEMMOV_OPS_DST_OFFSET (%r12), %rdi
    mov MEMMOV_OPS_SRC_OFFSET (%r12), %rsi
    mov MEMMOV_OPS_LEN_OFFSET (%r12), %rcx

    call .Lmexec_memmove

    /* Move the address of the bootdata into the appropriate register */
    mov %r8, %rsi

    /* Zero out some registers */ 
    xor %ebx, %ebx
    xor %edi, %edi
    xor %ebp, %ebp

    mov $ENTRY64_ADDR, %rbx
    mov (%rbx), %rax
    xor %rbx, %rbx

    /* Clear interrupts */
    cli

    /* See you on the other side! */
    jmp *%rax

    /* Crash, we should never reach here */
    ud2

.Lmexec_memmove:
    /* Move RCX bytes from RSI to RDI */
    cld               /* Clear the direction flag so that we're copying forward */
                      /* by default when we start */

    cmp %rsi, %rdi    /* Compare the src and dst registers to see if we need to */
                      /* copy forwards or backwards */

    jbe .Ldo_copy      /* if dst is greater than src, go ahead and do the copy */
                      /* forwards */

    mov %rcx, %rax    /* rcx and rax contain the number of bytes to be copied */
    sub $1,   %rax    /* Move rsi and rdi to the end of their respective buffers */
    add %rax, %rdi
    add %rax, %rsi

    std               /* Set the direction flag to 1. This will ensure that the */
                      /* copy happens from the back of the buffers to the front */

.Ldo_copy:

    rep movsb         /* copy RCX bytes from RSI to RDI */

    cld               /* Clear the direction flag since we may have polluted it */
                      /* if we did a copy backwards */
    ret

DATA(mexec_asm_end)
